home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 03 - 1987 / 03.07 Jul 87 / midi library LSP / LSPMIDI Source < prev    next >
Encoding:
Text File  |  1987-05-17  |  26.0 KB  |  802 lines  |  [TEXT/QED1]

  1. ; Low Level MIDI routines with time-stamping
  2. ; Written by Kirk Austin 5/17/87
  3. ; This code is in the public domain and is absolutely free
  4.  
  5. ; Serial Chip equates
  6. SCCRd        EQU    $1D8
  7. SCCWr        EQU    $1DC
  8. aData        EQU    6
  9. aCtl        EQU    2
  10. bData        EQU    4
  11. bCtl        EQU    0
  12. TBE        EQU    2
  13.  
  14. ;  Interrupt vector equates
  15. Lvl1DT        EQU    $192
  16. Lvl2DT        EQU    $1B2
  17. RxIntOffsetA    EQU    24
  18. TxIntOffsetA    EQU    16
  19. SpecRecCondA    EQU    28
  20. RxIntOffsetB    EQU    8
  21. TxIntOffsetB    EQU    0
  22. SpecRecCondB    EQU    12
  23.  
  24. ;  6522 equates
  25. VIA        EQU    $1D4
  26. vT1C        EQU    $800
  27. vT1CH        EQU    $A00
  28. vT1L        EQU    $C00
  29. vACR        EQU    $1600
  30. vIER        EQU    $1C00
  31.  
  32. ; XDEF all routines that need to be accessed externally.
  33.  
  34.     XDEF        InitSCCA
  35.     XDEF        InitSCCB
  36.     XDEF        TxMIDIA
  37.     XDEF        TxMIDIB
  38.     XDEF        RxMIDIA
  39.     XDEF        RxMIDIB
  40.     XDEF        ResetSCCA
  41.     XDEF        ResetSCCB
  42.     XDEF        InitTimer
  43.     XDEF        LoadTimer
  44.     XDEF        StartCounter
  45.     XDEF        GetCounter
  46.     XDEF        QuitTimer
  47.  
  48. ; These are the routines for the Modem Port
  49.  
  50.  
  51. ;    PROCEDURE InitSCCA;
  52. ; Call this routine at the beginning of your application if you are going
  53. ; to be using the modem port for MIDI information transfers.
  54. InitSCCA
  55.     MOVE        SR,-(SP)        ; Save interrupts
  56.     MOVEM.L        D0/A0-A2,-(SP)        ; Save registers
  57.     ORI        #$0300,SR        ; Disable interrupts
  58.  
  59.     MOVE.L        SCCRd,A1        ; Get base Read address
  60.     ADD        #aCtl,A1        ; Add offset for control
  61.     MOVE.B        (A1),D0            ; Dummy read
  62.     MOVE.L        (SP),(SP)        ; Delay
  63.     MOVE.L        SCCWr,A0        ; Get base Write address
  64.     ADD        #aCtl,A0        ; Add offset for control
  65.     MOVE.B        #9,(A0)            ; pointer for SCC reg 9
  66.     MOVE.L        (SP),(SP)        ; Delay
  67.     MOVE.B        #%10000000,(A0)        ; Reset channel
  68.     MOVE.L        (SP),(SP)        ; Delay
  69.     BSR        InitSCCChan        ; branch to common init routine
  70.  
  71. ; set up the interrupt vectors
  72.  
  73.     MOVE.L        #Lvl2DT,A0        ; get dispatch table pointer
  74.     MOVE        #RxIntOffsetA,D0    ; get offset to Rx vector
  75.     LEA        PRxIntHandA,A1        ; point to previous vector storage
  76.     MOVE.L        0(A0,D0),(A1)        ; save previous int vector
  77.     LEA        RxIntHandA,A1        ; set Rx vector
  78.     MOVE.L        A1,0(A0,D0)
  79.     MOVE        #TxIntOffsetA,D0    ; get offset to Tx vector
  80.     LEA        PTxIntHandA,A1        ; point to previous vector storage
  81.     MOVE.L        0(A0,D0),(A1)        ; save previous int vector
  82.     LEA        TxIntHandA,A1        ; set Tx vector
  83.     MOVE.L        A1,0(A0,D0)
  84.     MOVE        #SpecRecCondA,D0    ; get offset to Special vector
  85.     LEA        StubA,A1
  86.     MOVE.L        A1,0(A0,D0)
  87.  
  88. ; initialize the flags & pointers
  89.  
  90.     LEA        RxByteInA,A2        ; get the address
  91.     CLR        (A2)
  92.     LEA        RxByteOutA,A2        ; get the address
  93.     CLR        (A2)
  94.     LEA        RxQEmptyA,A2        ; get the address
  95.     MOVE        #$FFFF,(A2)
  96.     LEA        TxByteInA,A2        ; get the address
  97.     CLR        (A2)
  98.     LEA        TxByteOutA,A2        ; get the address
  99.     CLR        (A2)
  100.     LEA        TxQEmptyA,A2        ; get the address
  101.     MOVE        #$FFFF,(A2)
  102.  
  103.     MOVEM.L        (SP)+,D0/A0-A2        ; Restore registers
  104.     MOVE        (SP)+,SR        ; Restore interrupts
  105.     RTS                    ; and return
  106.  
  107. ; This is the common initialzation routine for both channels
  108.  
  109. InitSCCChan
  110.     MOVE.B        #4,(A0)            ; pointer for SCC reg 4
  111.     MOVE.L        (SP),(SP)        ; Delay
  112.     MOVE.B        #%10000100,(A0)        ; 32x clock, 1 stop bit
  113.     MOVE.L        (SP),(SP)        ; Delay
  114.     MOVE.B        #1,(A0)            ; pointer for SCC reg 1
  115.     MOVE.L        (SP),(SP)        ; Delay
  116.     MOVE.B        #%00000000,(A0)        ; No W/Req
  117.     MOVE.L        (SP),(SP)        ; Delay
  118.     MOVE.B        #3,(A0)            ; pointer for SCC reg 3
  119.     MOVE.L        (SP),(SP)        ; Delay
  120.     MOVE.B        #%00000000,(A0)        ; Turn off Rx
  121.     MOVE.L        (SP),(SP)        ; Delay
  122.     MOVE.B        #5,(A0)            ; pointer for SCC reg 5
  123.     MOVE.L        (SP),(SP)        ; Delay
  124.     MOVE.B        #%00000000,(A0)        ; Turn off Tx
  125.     MOVE.L        (SP),(SP)        ; Delay
  126.     MOVE.B        #11,(A0)        ; pointer for SCC reg 11
  127.     MOVE.L        (SP),(SP)        ; Delay
  128.     MOVE.B        #%00101000,(A0)        ; Make TRxC clock source
  129.     MOVE.L        (SP),(SP)        ; Delay
  130.     MOVE.B        #14,(A0)        ; pointer for SCC reg 14
  131.     MOVE.L        (SP),(SP)        ; Delay
  132.     MOVE.B        #%00000000,(A0)        ; Disable BRGen
  133.     MOVE.L        (SP),(SP)        ; Delay
  134.     MOVE.B        #3,(A0)            ; pointer for SCC reg 3
  135.     MOVE.L        (SP),(SP)        ; Delay
  136.     MOVE.B        #%11000001,(A0)        ; Enable Rx
  137.     MOVE.L        (SP),(SP)        ; Delay
  138.     MOVE.B        #5,(A0)            ; pointer for SCC reg 5
  139.     MOVE.L        (SP),(SP)        ; Delay
  140.     MOVE.B        #%01101010,(A0)        ; Enable Tx and drivers
  141.     MOVE.L        (SP),(SP)        ; Delay
  142.     MOVE.B        #15,(A0)        ; pointer for SCC reg 15
  143.     MOVE.L        (SP),(SP)        ; Delay
  144.     MOVE.B        #%00001000,(A0)        ; Enable DCD int for mouse
  145.     MOVE.L        (SP),(SP)        ; Delay
  146.     MOVE.B        #0,(A0)            ; pointer for SCC reg 0
  147.     MOVE.L        (SP),(SP)        ; Delay
  148.     MOVE.B        #%00010000,(A0)        ; Reset EXT/STATUS
  149.     MOVE.L        (SP),(SP)        ; Delay
  150.     MOVE.B        #0,(A0)            ; pointer for SCC reg 0
  151.     MOVE.L        (SP),(SP)        ; Delay
  152.     MOVE.B        #%00010000,(A0)        ; Reset EXT/STATUS again
  153.     MOVE.L        (SP),(SP)        ; Delay
  154.     MOVE.B        #1,(A0)            ; pointer for SCC reg 1
  155.     MOVE.L        (SP),(SP)        ; Delay
  156.     MOVE.B        #%00010011,(A0)        ; Enable interrupts
  157.     MOVE.L        (SP),(SP)        ; Delay
  158.     MOVE.B        #9,(A0)            ; pointer for SCC reg 9
  159.     MOVE.L        (SP),(SP)        ; Delay
  160.     MOVE.B        #%00001010,(A0)        ; Set master int enable
  161.     MOVE.L        (SP),(SP)        ; Delay
  162.     RTS
  163.     
  164. ;    PROCEDURE TxMIDIA (TheData : integer);
  165. ; This is the routine to transmit a MIDI byte of data through the Modem Port.
  166. ; To use this routine place the byte to be transmitted as the lower 8 bits
  167. ; of a word on the stack, then call TxMIDIA.
  168. TxMIDIA
  169.     LINK        A6,#0            ; set frame pointer
  170.     MOVE        SR,-(SP)        ; Save interrupts
  171.     MOVEM.L        D0/A0-A3,-(SP)        ; Save registers
  172.     ORI        #$0300,SR        ; Disable interrupts
  173.  
  174.     LEA        TxQEmptyA,A3        ; get the address
  175.     TST.B        (A3)            ; is TxQueue empty?
  176.     BNE        TxQEA            ; if so branch
  177.     LEA        TxByteInA,A3        ; get the address
  178.     MOVE        (A3),D0            ; if not add byte to queue
  179.     LEA        TxQueueA,A2        ; point to queue
  180.     MOVE.B        9(A6),0(A2,D0)        ; place byte in queue
  181.     ADDQ        #1,D0            ; update TxByteIn
  182.     CMP        #$100,D0
  183.     BNE        @1
  184.     MOVE        #0,D0
  185. @1    MOVE        D0,(A3)
  186.     BRA        TxExitA            ; and exit
  187.     
  188. TxQEA    MOVE.L        SCCRd,A0        ; get SCC Read Address
  189.     MOVE.L        SCCWr,A1        ; get SCC Write address
  190.     MOVE        #aCtl,D0        ; get index for Ctl
  191.     BTST.B        #TBE,0(A0,D0)        ; transmit buffer empty?
  192.     BNE        FirstByteA        ; if so branch
  193.     LEA        TxByteInA,A3        ; get the address
  194.     MOVE        (A3),D0            ; if not add to queue
  195.     LEA        TxQueueA,A2        ; point to queue
  196.     MOVE.B        9(A6),0(A2,D0)        ; place byte in queue
  197.     ADDQ        #1,D0            ; update pointer
  198.     CMP        #$100,D0
  199.     BNE        @1
  200.     MOVE        #0,D0
  201. @1    MOVE        D0,(A3)
  202.     LEA        TxQEmptyA,A3        ; get the address
  203.     MOVE        #0,(A3)            ; reset queue empty flag
  204.     BRA        TxExitA            ; and exit
  205.         
  206. FirstByteA
  207.     MOVE        #aData,D0        ; get index to data
  208.     MOVE.L        (SP),(SP)        ; delay
  209.     MOVE.B        9(A6),0(A1,D0)        ; write data to SCC
  210.     MOVE.L        (SP),(SP)        ; Delay
  211.     
  212.  
  213. TxExitA    MOVEM.L        (SP)+,D0/A0-A3        ; Restore registers
  214.     MOVE        (SP)+,SR        ; Restore interrupts
  215.     UNLK        A6            ; release frame pointer
  216.     MOVE.L        (SP)+,A1        ; save return address
  217.     ADD.L        #2,SP            ; move past data word
  218.     MOVE.L        A1,-(SP)        ; put address back on stack
  219.     RTS                    ; and return
  220.  
  221. ;    FUNCTION RxMIDIA : LongInt;
  222. ; This routine gets a byte through the modem port.  To use this routine
  223. ; treat it like a Pascal function.  Leave space on the stack for a longword
  224. ; of data before calling this routine.  If the data on the stack after
  225. ; the routine executes is 0 there was no MIDI data available.  If it's non-0
  226. ; the upper 3 bytes contain the counter value, the MIDI byte is the low byte.
  227. RxMIDIA
  228.     LINK        A6,#0            ; set frame pointer
  229.     MOVE        SR,-(SP)        ; Save interrupts
  230.     MOVEM.L        D0-D1/A0-A3,-(SP)    ; Save registers
  231.     ORI        #$0300,SR        ; disable interrupts
  232.  
  233.     LEA        RxQEmptyA,A3        ; get the address
  234.     TST.B        (A3)            ; any data available?
  235.     BEQ        @1            ; if so, branch
  236.     MOVE.L        #0,8(A6)        ; if not, return with 0
  237.     BRA        RxExitA
  238. @1    LEA        RxByteOutA,A3        ; get the address
  239.     MOVE        (A3),D0            ; get index to byte out
  240.     LEA        RxQueueA,A2        ; point to queue
  241.     MOVE.L        #0,D1            ; clear data register
  242.     MOVE.L        0(A2,D0),D1        ; get MIDI data
  243.     MOVE.L        D1,8(A6)        ; place it on stack for return 
  244.     ADDQ        #4,D0            ; update index
  245.     CMP        #$400,D0
  246.     BNE        @2
  247.     MOVE        #0,D0
  248. @2    LEA        RxByteOutA,A3        ; get the address
  249.     MOVE        D0,(A3)
  250.     LEA        RxByteInA,A3        ; get the address
  251.     MOVE        (A3),D1
  252.     CMP        D0,D1            ; is queue empty?
  253.     BNE        RxExitA            ; if not exit
  254.     LEA        RxQEmptyA,A3        ; get the address
  255.     MOVE        #$FFFF,(A3)        ; if empty, set flag
  256.  
  257. RxExitA    MOVEM.L        (SP)+,D0-D1/A0-A3    ; Restore registers
  258.     MOVE        (SP)+,SR        ; restore interrupts
  259.     UNLK        A6
  260.     RTS                    ; and return
  261.     
  262. ; This is the interrupt routine for receiving through the modem port. 
  263. ; It places the counter value and the MIDI byte in a circular queue to be
  264. ; accessed later by the application.
  265. ; When the system gets this far, A0 contains the SCC base read Ctl address
  266. ; and A1 contains the SCC base write Ctl address for this channel.
  267. ; The data addresses are offset by 4 from the control addresses.
  268. ; D0-D3/A0-A3 are already preserved, so they may be used freely.
  269. RxIntHandA
  270.     ORI        #$0300,SR        ; disable interrupts
  271.  
  272. @3    MOVE        #4,D0            ; get data offset
  273.     CLR.L        D1            ; prepare for data
  274.     MOVE.L        (SP),(SP)        ; Delay
  275.     MOVE.B        0(A0,D0),D1        ; read data from SCC
  276.     MOVE.L        (SP),(SP)        ; Delay
  277.     LEA        RxQueueA,A2        ; point to queue
  278.     LEA        RxByteInA,A3        ; get the address
  279.     MOVE        (A3),D0            ; get offset to next cell
  280.     LEA        Counter,A3        ; get the address
  281.     MOVE.L        (A3),D2            ; put counter value in D2
  282.     LSL.L        #8,D2            ; shift counter one byte
  283.     ADD.L        D2,D1            ; combine counter and data
  284.     MOVE.L        D1,0(A2,D0)        ; put longword in queue
  285.     LEA        RxQEmptyA,A3        ; get the address
  286.     MOVE        #0,(A3)            ; reset queue empty flag
  287.     ADDQ        #4,D0            ; update index
  288.     CMP        #$400,D0
  289.     BNE        @1
  290.     MOVE        #0,D0
  291. @1    LEA        RxByteInA,A3        ; get the address
  292.     MOVE        D0,(A3)
  293.  
  294. @2    BTST.B        #0,(A0)            ; is there more data?
  295.     BNE        @3            ; do it again if there is
  296.  
  297.     ANDI        #$F8FF,SR        ; enable interrupts
  298.     RTS                    ; and return
  299.     
  300. ; This is the interrupt routine for transmitting a byte through the modem port.
  301. ; It checks to see if there is any data to send, and if there is it sends it to 
  302. ; the SCC.  If there isn't it resets the TBE interrupt in the SCC and exits.
  303. ; When the system gets this far, A0 contains the SCC base read Ctl address
  304. ; and A1 contains the SCC base write Ctl address for this channel.
  305. ; The data addresses are offset by 4 from the control addresses.
  306. ; D0-D3/A0-A3 are already preserved, so they may be used freely.
  307. TxIntHandA
  308.     ORI        #$0300,SR        ; disable interrupts
  309.     
  310.     LEA        TxQEmptyA,A3        ; get the address
  311.     TST.B        (A3)            ; Is queue empty?
  312.     BEQ        @1            ; if not branch
  313.     MOVE.B        #$28,(A1)        ; if so, reset TBE interrupt
  314.     MOVE.L        (SP),(SP)        ; Delay
  315.     BRA        TxIExitA        ; and exit
  316. @1    LEA        TxByteOutA,A3        ; get the address
  317.     MOVE        (A3),D0            ; get index to next data byte
  318.     LEA        TxQueueA,A2        ; point to queue
  319.     MOVE        #4,D1            ; get data offset
  320.     MOVE.B        0(A2,D0),0(A1,D1)    ; write data to SCC
  321.     MOVE.L        (SP),(SP)        ; Delay
  322.     ADDQ        #1,D0            ; update index
  323.     CMP        #$100,D0
  324.     BNE        @2
  325.     MOVE        #0,D0
  326. @2    LEA        TxByteOutA,A3        ; get the address
  327.     MOVE        D0,(A3)
  328.     LEA        TxByteInA,A3        ; get the address
  329.     MOVE        (A3),D1
  330.     CMP        D0,D1            ; is TxQueue empty?
  331.     BNE        TxIExitA        ; if not exit
  332.     LEA        TxQEmptyA,A3        ; get the address
  333.     MOVE        #$FFFF,(A3)        ; if empty set flag
  334.  
  335. TxIExitA
  336.     ANDI        #$F8FF,SR        ; enable interrupts
  337.     RTS                    ; and return
  338.     
  339.  
  340. ;    PROCEDURE ResetSCCA;
  341. ; If you called InitSCCA at the beginning of your application this
  342. ; routine must be called when the application quits or the system will
  343. ; crash due to the interrupt handling pointers becoming invalid.
  344. ResetSCCA
  345.     MOVEM.L        D0/A0-A1,-(SP)        ; save registers
  346.     MOVE        SR,-(SP)        ; Save interrupts
  347.     ORI        #$0300,SR        ; Disable interrupts
  348.     
  349.     MOVE.L        SCCWr,A0        ; Get base Write address
  350.     ADD        #aCtl,A0        ; Add offset for control
  351.     MOVE.B        #9,(A0)            ; pointer for SCC reg 9
  352.     MOVE.L        (SP),(SP)        ; Delay
  353.     MOVE.B        #%10000000,(A0)        ; Reset channel
  354.     MOVE.L        (SP),(SP)        ; Delay
  355.     BSR        ResetSCCChan        ; branch to the common reset routine
  356.  
  357.     MOVE.L        #Lvl2DT,A0        ; get dispatch table pointer
  358.     MOVE        #RxIntOffsetA,D0    ; get offset to Rx vector
  359.     LEA        PRxIntHandA,A1        ; point to previous vector storage
  360.     MOVE.L        (A1),0(A0,D0)        ; restore previous int vector
  361.     MOVE        #TxIntOffsetA,D0    ; get offset to Tx vector
  362.     LEA        PTxIntHandA,A1        ; set Rx vector
  363.     MOVE.L        (A1),0(A0,D0)        ; restore previous int vector
  364.  
  365.     MOVE        (SP)+,SR        ; Restore interrupts
  366.     MOVEM.L        (SP)+,D0/A0-A1        ; restore registers
  367.     RTS                    ; and return
  368.  
  369. ; This is the common reset routine for both channels
  370.  
  371. ResetSCCChan
  372.     MOVE.B        #15,(A0)        ; pointer for SCC reg 15
  373.     MOVE.L        (SP),(SP)        ; Delay
  374.     MOVE.B        #%00001000,(A0)        ; Enable DCD int
  375.     MOVE.L        (SP),(SP)        ; Delay
  376.     MOVE.B        #0,(A0)            ; pointer for SCC reg 0
  377.     MOVE.L        (SP),(SP)        ; Delay
  378.     MOVE.B        #%00010000,(A0)        ; Reset EXT/STATUS
  379.     MOVE.L        (SP),(SP)        ; Delay
  380.     MOVE.B        #0,(A0)            ; pointer for SCC reg 0
  381.     MOVE.L        (SP),(SP)        ; Delay
  382.     MOVE.B        #%00010000,(A0)        ; Reset EXT/STATUS again
  383.     MOVE.L        (SP),(SP)        ; Delay
  384.     MOVE.B        #1,(A0)            ; pointer for SCC reg 1
  385.     MOVE.L        (SP),(SP)        ; Delay
  386.     MOVE.B        #%00000001,(A0)        ; Enable mouse interrupts
  387.     MOVE.L        (SP),(SP)        ; Delay
  388.     MOVE.B        #9,(A0)            ; pointer for SCC reg 9
  389.     MOVE.L        (SP),(SP)        ; Delay
  390.     MOVE.B        #%00001010,(A0)        ; Set master int enable
  391.     MOVE.L        (SP),(SP)        ; Delay
  392.     RTS
  393.  
  394. TxQueueA        DCB.B    $100,0        ; this is the queue
  395. TxQEmptyA        DC    0        ; the queue empty flag
  396. TxByteInA        DC    0        ; index to next cell in
  397. TxByteOutA        DC    0        ; index to next cell out
  398. RxQueueA        DCB.B    $400,0        ; this is the queue
  399. RxQEmptyA        DC    0        ; the queue empty flag
  400. RxByteInA        DC    0        ; index to next cell in
  401. RxByteOutA        DC    0        ; index to next cell out
  402. PRxIntHandA        DC.L    0        ; Previous interrupt vector
  403. PTxIntHandA        DC.L    0        ; Previous interrupt vector
  404.  
  405. ; These are the routines for the Printer Port
  406.  
  407. ;    PROCEDURE InitSCCB;
  408. ; Call this routine at the beginning of your application if you are going
  409. ; to be using the printer port for MIDI information transfers.
  410. InitSCCB
  411.  
  412.     MOVE        SR,-(SP)        ; Save interrupts
  413.     MOVEM.L        D0/A0-A2,-(SP)        ; Save registers
  414.     ORI        #$0300,SR        ; Disable interrupts
  415.     
  416.     MOVE.L        SCCRd,A1        ; Get base Read address
  417.     ADD        #bCtl,A1        ; Add offset for control
  418.     MOVE.B        (A1),D0            ; Dummy read
  419.     MOVE.L        (SP),(SP)        ; Delay
  420.     MOVE.L        SCCWr,A0        ; Get base Write address
  421.     ADD        #bCtl,A0        ; Add offset for control
  422.     MOVE.B        #9,(A0)            ; pointer for SCC reg 9
  423.     MOVE.L        (SP),(SP)        ; Delay
  424.     MOVE.B        #%01000000,(A0)        ; Reset channel
  425.     MOVE.L        (SP),(SP)        ; Delay
  426.     BSR        InitSCCChan        ; branch to common init routine
  427.     
  428. ; set up the interrupt vectors
  429.  
  430.     MOVE.L        #Lvl2DT,A0        ; get dispatch table pointer
  431.     MOVE        #RxIntOffsetB,D0    ; get offset to Rx vector
  432.     LEA        PRxIntHandB,A1        ; point to previous vector storage
  433.     MOVE.L        0(A0,D0),(A1)        ; save previous int vector
  434.     LEA        RxIntHandB,A1        ; set Rx vector
  435.     MOVE.L        A1,0(A0,D0)
  436.     MOVE        #TxIntOffsetB,D0    ; get offset to Tx vector
  437.     LEA        PTxIntHandB,A1        ; set Rx vector
  438.     MOVE.L        0(A0,D0),(A1)        ; save previous int vector
  439.     LEA        TxIntHandB,A1        ; set Tx vector
  440.     MOVE.L        A1,0(A0,D0)
  441.     MOVE        #SpecRecCondB,D0    ; get offset to Special vector
  442.     LEA        StubB,A1
  443.     MOVE.L        A1,0(A0,D0)
  444.     
  445. ; initialize the flags & pointers
  446.  
  447.     LEA        RxByteInB,A2        ; get the address
  448.     CLR        (A2)
  449.     LEA        RxByteOutB,A2        ; get the address
  450.     CLR        (A2)
  451.     LEA        RxQEmptyB,A2        ; get the address
  452.     MOVE        #$FFFF,(A2)
  453.     LEA        TxByteInB,A2        ; get the address
  454.     CLR        (A2)
  455.     LEA        TxByteOutB,A2        ; get the address
  456.     CLR        (A2)
  457.     LEA        TxQEmptyB,A2        ; get the address
  458.     MOVE        #$FFFF,(A2)
  459.  
  460.     MOVEM.L        (SP)+,D0/A0-A2        ; Restore registers
  461.     MOVE        (SP)+,SR        ; Restore interrupts
  462.     RTS                    ; and return
  463.  
  464. ;    PROCEDURE TxMIDIB (TheData : integer);
  465. ; This is the routine to transmit a MIDI byte of data through the Printer Port.
  466. ; To use this routine place the byte to be transmitted as the lower 8 bits
  467. ; of a word on the stack, then call TxMIDIB.
  468. TxMIDIB
  469.     LINK        A6,#0            ; set frame pointer
  470.     MOVE        SR,-(SP)        ; Save interrupts
  471.     MOVEM.L        D0/A0-A3,-(SP)        ; Save registers
  472.     ORI        #$0300,SR        ; Disable interrupts
  473.  
  474.     LEA        TxQEmptyB,A3        ; get the address
  475.     TST.B        (A3)            ; is TxQueue empty?
  476.     BNE        TxQEB            ; if so branch
  477.     LEA        TxByteInB,A3        ; get the address
  478.     MOVE        (A3),D0            ; if not add byte to queue
  479.     LEA        TxQueueB,A2        ; point to queue
  480.     MOVE.B        9(A6),0(A2,D0)        ; place byte in queue
  481.     ADDQ        #1,D0            ; update TxByteIn
  482.     CMP        #$100,D0
  483.     BNE        @1
  484.     MOVE        #0,D0
  485. @1    MOVE        D0,(A3)
  486.     BRA        TxExitB            ; and exit
  487.     
  488. TxQEB    MOVE.L        SCCRd,A0        ; get SCC Read Address
  489.     MOVE.L        SCCWr,A1        ; get SCC Write address
  490.     MOVE        #bCtl,D0        ; get index for Ctl
  491.     BTST.B        #TBE,0(A0,D0)        ; transmit buffer empty?
  492.     BNE        FirstByteB        ; if so branch
  493.     LEA        TxByteInB,A3        ; get the address
  494.     MOVE        (A3),D0            ; if not add to queue
  495.     LEA        TxQueueB,A2        ; point to queue
  496.     MOVE.B        9(A6),0(A2,D0)        ; place byte in queue
  497.     ADDQ        #1,D0            ; update pointer
  498.     CMP        #$100,D0
  499.     BNE        @1
  500.     MOVE        #0,D0
  501. @1    MOVE        D0,(A3)
  502.     LEA        TxQEmptyB,A3        ; get the address
  503.     MOVE        #0,(A3)            ; reset queue empty flag
  504.     BRA        TxExitB            ; and exit
  505.         
  506. FirstByteB
  507.     MOVE        #bData,D0        ; get index to data
  508.     MOVE.L        (SP),(SP)        ; delay
  509.     MOVE.B        9(A6),0(A1,D0)        ; write data to SCC
  510.     MOVE.L        (SP),(SP)        ; Delay
  511.     
  512.  
  513. TxExitB    MOVEM.L        (SP)+,D0/A0-A3        ; Restore registers
  514.     MOVE        (SP)+,SR        ; Restore interrupts
  515.     UNLK        A6            ; release frame pointer
  516.     MOVE.L        (SP)+,A1        ; save return address
  517.     ADD.L        #2,SP            ; move past data word
  518.     MOVE.L        A1,-(SP)        ; put address back on stack
  519.     RTS                    ; and return
  520.  
  521. ;    FUNCTION RxMIDIB : LongInt;
  522. ; This routine gets a byte through the printer port.  To use this routine
  523. ; treat it like a Pascal function.  Leave space on the stack for a longword
  524. ; of data before calling this routine.  If the data on the stack after
  525. ; the routine executes is 0 there was no MIDI data available.  If it's non-0
  526. ; the upper 3 bytes contain the counter value, the MIDI byte is the low byte.
  527. RxMIDIB
  528.     LINK        A6,#0            ; set frame pointer
  529.     MOVE        SR,-(SP)        ; Save interrupts
  530.     MOVEM.L        D0-D1/A0-A3,-(SP)    ; Save registers
  531.     ORI        #$0300,SR        ; disable interrupts
  532.  
  533.     LEA        RxQEmptyB,A3        ; get the address
  534.     TST.B        (A3)            ; any data available?
  535.     BEQ        @1            ; if so, branch
  536.     MOVE.L        #0,8(A6)        ; if not, return with 0
  537.     BRA        RxExitB
  538. @1    LEA        RxByteOutB,A3        ; get the address
  539.     MOVE        (A3),D0            ; get index to byte out
  540.     LEA        RxQueueB,A2        ; point to queue
  541.     MOVE.L        #0,D1            ; clear data register
  542.     MOVE.L        0(A2,D0),D1        ; get MIDI data
  543.     MOVE.L        D1,8(A6)        ; place it on stack for return 
  544.     ADDQ        #4,D0            ; update index
  545.     CMP        #$400,D0
  546.     BNE        @2
  547.     MOVE        #0,D0
  548. @2    LEA        RxByteOutB,A3        ; get the address
  549.     MOVE        D0,(A3)
  550.     LEA        RxByteInB,A3        ; get the address
  551.     MOVE        (A3),D1
  552.     CMP        D0,D1            ; is queue empty?
  553.     BNE        RxExitB            ; if not exit
  554.     LEA        RxQEmptyB,A3        ; get the address
  555.     MOVE        #$FFFF,(A3)        ; if empty, set flag
  556.  
  557. RxExitB    MOVEM.L        (SP)+,D0-D1/A0-A3    ; Restore registers
  558.     MOVE        (SP)+,SR        ; restore interrupts
  559.     UNLK        A6
  560.     RTS                    ; and return
  561.     
  562. ; This is the interrupt routine for receiving through the printer port. 
  563. ; It places the counter value and the MIDI byte in a circular queue to be
  564. ; accessed later by the application.
  565. ; When the system gets this far, A0 contains the SCC base read Ctl address
  566. ; and A1 contains the SCC base write Ctl address for this channel.
  567. ; The data addresses are offset by 4 from the control addresses.
  568. ; D0-D3/A0-A3 are already preserved, so they may be used freely.
  569. RxIntHandB
  570.     ORI        #$0300,SR        ; disable interrupts
  571.     
  572. @3    MOVE        #4,D0            ; get data offset
  573.     CLR.L        D1            ; prepare for data
  574.     MOVE.L        (SP),(SP)        ; Delay
  575.     MOVE.B        0(A0,D0),D1        ; read data from SCC
  576.     MOVE.L        (SP),(SP)        ; Delay
  577.     LEA        RxQueueB,A2        ; point to queue
  578.     LEA        RxByteInB,A3        ; get the address
  579.     MOVE        (A3),D0            ; get offset to next cell
  580.     LEA        Counter,A3        ; get the address
  581.     MOVE.L        (A3),D2            ; put counter value in D2
  582.     LSL.L        #8,D2            ; shift counter one byte
  583.     ADD.L        D2,D1            ; combine counter and data
  584.     MOVE.L        D1,0(A2,D0)        ; put longword in queue
  585.     LEA        RxQEmptyB,A3        ; get the address
  586.     MOVE        #0,(A3)            ; reset queue empty flag
  587.     ADDQ        #4,D0            ; update index
  588.     CMP        #$400,D0
  589.     BNE        @1
  590.     MOVE        #0,D0
  591. @1    LEA        RxByteInB,A3        ; get the address
  592.     MOVE        D0,(A3)
  593.     
  594. @2    BTST.B        #0,(A0)            ; is there more data?
  595.     BNE        @3            ; do it again if there is
  596.  
  597.     ANDI        #$F8FF,SR        ; enable interrupts
  598.     RTS                    ; and return
  599.     
  600. ; This is the interrupt routine for transmitting a byte through the printer port.
  601. ; It checks to see if there is any data to send, and if there is it sends it to 
  602. ; the SCC.  If there isn't it resets the TBE interrupt in the SCC and exits.
  603. ; When the system gets this far, A0 contains the SCC base read Ctl address
  604. ; and A1 contains the SCC base write Ctl address for this channel.
  605. ; The data addresses are offset by 4 from the control addresses.
  606. ; D0-D3/A0-A3 are already preserved, so they may be used freely.
  607. TxIntHandB
  608.     ORI        #$0300,SR        ; disable interrupts
  609.     
  610.     LEA        TxQEmptyB,A3        ; get the address
  611.     TST.B        (A3)            ; Is queue empty?
  612.     BEQ        @1            ; if not branch
  613.     MOVE.B        #$28,(A1)        ; if so, reset TBE interrupt
  614.     MOVE.L        (SP),(SP)        ; Delay
  615.     BRA        TxIExitB        ; and exit
  616. @1    LEA        TxByteOutB,A3        ; get the address
  617.     MOVE        (A3),D0            ; get index to next data byte
  618.     LEA        TxQueueB,A2        ; point to queue
  619.     MOVE        #4,D1            ; get data offset
  620.     MOVE.B        0(A2,D0),0(A1,D1)    ; write data to SCC
  621.     MOVE.L        (SP),(SP)        ; Delay
  622.     ADDQ        #1,D0            ; update index
  623.     CMP        #$100,D0
  624.     BNE        @2
  625.     MOVE        #0,D0
  626. @2    LEA        TxByteOutB,A3        ; get the address
  627.     MOVE        D0,(A3)
  628.     LEA        TxByteInB,A3        ; get the address
  629.     MOVE        (A3),D1
  630.     CMP        D0,D1            ; is TxQueue empty?
  631.     BNE        TxIExitB        ; if not exit
  632.     LEA        TxQEmptyB,A3        ; get the address
  633.     MOVE        #$FFFF,(A3)        ; if empty set flag
  634.  
  635. TxIExitB
  636.     ANDI        #$F8FF,SR        ; enable interrupts
  637.     RTS                    ; and return
  638.     
  639. ;    PROCEDURE ResetSCCB;
  640. ; If you called InitSCCB at the beginning of your application this
  641. ; routine must be called when the application quits or the system will
  642. ; crash due to the interrupt handling pointers becoming invalid.
  643. ResetSCCB
  644.     MOVEM.L        D0/A0-A1,-(SP)        ; save registers
  645.     MOVE        SR,-(SP)        ; Save interrupts
  646.     ORI        #$0300,SR        ; Disable interrupts
  647.     
  648.     MOVE.L        SCCWr,A0        ; Get base Write address
  649.     ADD        #bCtl,A0        ; Add offset for control
  650.     MOVE.B        #9,(A0)            ; pointer for SCC reg 9
  651.     MOVE.L        (SP),(SP)        ; Delay
  652.     MOVE.B        #%01000000,(A0)        ; Reset channel
  653.     MOVE.L        (SP),(SP)        ; Delay
  654.     BSR        ResetSCCChan        ; branch to the common reset routine
  655.     
  656.     MOVE.L        #Lvl2DT,A0        ; get dispatch table pointer
  657.     MOVE        #RxIntOffsetB,D0    ; get offset to Rx vector
  658.     LEA        PRxIntHandB,A1        ; point to previous vector storage
  659.     MOVE.L        (A1),0(A0,D0)        ; restore previous int vector
  660.     MOVE        #TxIntOffsetB,D0    ; get offset to Tx vector
  661.     LEA        PTxIntHandB,A1        ; set Rx vector
  662.     MOVE.L        (A1),0(A0,D0)        ; restore previous int vector
  663.  
  664.     MOVE        (SP)+,SR        ; Restore interrupts
  665.     MOVEM.L        (SP)+,D0/A0-A1        ; restore registers
  666.     RTS                    ; and return
  667.  
  668. TxQueueB        DCB.B    $100,0        ; this is the queue
  669. TxQEmptyB        DC    0        ; the queue empty flag
  670. TxByteInB        DC    0        ; index to next cell in
  671. TxByteOutB        DC    0        ; index to next cell out
  672. RxQueueB        DCB.B    $400,0        ; this is the queue
  673. RxQEmptyB        DC    0        ; the queue empty flag
  674. RxByteInB        DC    0        ; index to next cell in
  675. RxByteOutB        DC    0        ; index to next cell out
  676. PRxIntHandB        DC.L    0        ; Previous interrupt vector
  677. PTxIntHandB        DC.L    0        ; Previous interrupt vector
  678.  
  679. ; This is the space for a special condition interrupt routine.
  680. ; All I do here is reset the error flag in the SCC and return.
  681. ; When the system gets this far, A0 contains the SCC base read Ctl address
  682. ; and A1 contains the SCC base write Ctl address for this channel.
  683. ; The data addresses are offset by 4 from the control addresses.
  684. ; D0-D3/A0-A3 are already preserved, so they may be used freely.
  685. StubA
  686.     ORI        #$0300,SR        ; Disable interrupts
  687.     MOVE.B        #%00110000,(A1)        ; Reset Error
  688.     MOVE.L        (SP),(SP)        ; Delay
  689.     ANDI        #$F8FF,SR        ; Restore interrupts
  690.  
  691.     RTS
  692.  
  693. ; This is the space for a special condition interrupt routine.
  694. ; All I do here is reset the error flag in the SCC and return.
  695. ; When the system gets this far, A0 contains the SCC base read Ctl address
  696. ; and A1 contains the SCC base write Ctl address for this channel.
  697. ; The data addresses are offset by 4 from the control addresses.
  698. ; D0-D3/A0-A3 are already preserved, so they may be used freely.
  699. StubB
  700.     ORI        #$0300,SR        ; Disable interrupts
  701.     MOVE.B        #%00110000,(A1)        ; Reset Error
  702.     MOVE.L        (SP),(SP)        ; Delay
  703.     ANDI        #$F8FF,SR        ; Restore interrupts
  704.  
  705.     RTS
  706.  
  707. ; These are the routines for the counter you can use for time-stamping the
  708. ; incoming MIDI data.  This is useful for writing sequencer type applications.
  709. ; The time-stamping is done on an interrupt level, is extremely accurate,
  710. ; and uses the VIA timer #1.  This means that you can't use any of the
  711. ; Sound Manager routines because they use timer #1 too.  If you want to
  712. ; create a metronome click you have to write your own code that accesses
  713. ; the sound hardware directly without using timer #1.
  714. ; InitTimer and LoadTimer expect a word on the stack to load the timer.
  715. ; To increment the counter every millisecond, load the timer with decimal 782
  716. ; If you aren't going to use time-stamping you can ignore these routines.
  717.  
  718. ;    PROCEDURE InitTimer (TimrValue : integer);
  719. ; Only call InitTimer once at the beginning of your application
  720. ; 1 millisecond is decimal 782.
  721. InitTimer
  722.     LINK        A6,#0            ; set frame pointer
  723.     MOVEM.L        D0/A0-A1,-(SP)
  724.     MOVE.L        #Lvl1DT,A0        ; Point to level 1 dispatch table
  725.     LEA        PrevIVC,A1        ; point to interrupt vector storage
  726.     MOVE.L        24(A0),(A1)        ; save the previous interrupt vector
  727.     LEA        CounterIntHand,A1    ; point to the new interrupt handler
  728.     MOVE.L        A1,24(A0)        ; put it in the dispatch table
  729.     MOVE.L        VIA,A1            ; point to the 6522 chip
  730.     ORI.B        #$40,vACR(A1)        ; set the timer to freerun mode
  731.     MOVE.B        #$C0,vIER(A1)        ; Enable timer interrupts
  732.     MOVE        8(A6),D0        ; Get timer value
  733.     MOVE.B        D0,vT1L(A1)        ; set timer lo byte
  734.     LSR        #8,D0            ; shift to hi byte
  735.     MOVE.B        D0,vT1CH(A1)        ; set timer hi byte
  736.     MOVEM.L        (SP)+,D0/A0-A1
  737.     UNLK        A6
  738.     MOVE.L        (SP)+,A0        ; save return address
  739.     ADDQ        #2,SP            ; move past timer value
  740.     MOVE.L        A0,-(SP)        ; replace return address
  741.     RTS
  742.  
  743. ;    PROCEDURE LoadTimer (TimrValue : integer);
  744. ; Call LoadTimer whenever you want to change the timer value.
  745. ; 1 millisecond is decimal 782.
  746. LoadTimer
  747.     LINK        A6,#0            ; set frame pointer
  748.     MOVEM.L        D0/A0-A1,-(SP)
  749.     MOVE.L        VIA,A1            ; point to the 6522 chip
  750.     MOVE        8(A6),D0        ; Get timer value
  751.     MOVE.B        D0,vT1L(A1)        ; set timer lo byte
  752.     LSR        #8,D0            ; shift to hi byte
  753.     MOVE.B        D0,vT1CH(A1)        ; set timer hi byte
  754.     MOVEM.L        (SP)+,D0/A0-A1
  755.     UNLK        A6
  756.     MOVE.L        (SP)+,A0        ; save return address
  757.     ADDQ        #2,SP            ; move past timer value
  758.     MOVE.L        A0,-(SP)        ; replace return address
  759.     RTS
  760.  
  761. ;    PROCEDURE StartCounter;
  762. ; StartCounter sets the counter value to 1
  763. StartCounter
  764.     LEA        Counter,A0        ; point to the counter
  765.     MOVE.L        #1,(A0)            ; set it to 1
  766.     RTS
  767.  
  768. ;    FUNCTION GetCounter : LongInt;
  769. ; GetCounter returns a longword that is the value of the counter    
  770. GetCounter
  771.     MOVE.L        A0,-(SP)
  772.     LEA        Counter,A0        ; point to the counter
  773.     MOVE.L        (A0),8(SP)        ; return it as the function result
  774.     MOVE.L        (SP)+,A0
  775.     RTS
  776.  
  777. ;    PROCEDURE QuitTimer;
  778. ; Call QuitTimer when your application is done or the system will crash.
  779. QuitTimer
  780.     MOVEM.L        A0-A1,-(SP)
  781.     MOVE.L        VIA,A1            ; Disable 6522 interrupts
  782.     MOVE.B        #$40,vIER(A1)
  783.     LEA        PrevIVC,A1        ; Restore previous interrupt vector
  784.     MOVE.L        #Lvl1dt,A0
  785.     MOVE.L        (A1),24(A0)
  786.     MOVEM.L        (SP)+,A0-A1
  787.     RTS
  788.  
  789. ; This is the interrupt handler routine for the counter.  When the
  790. ; system gets this far A1 contains the base address of the VIA.
  791. ; It also preserves D0-D3/A0-A3.
  792. CounterIntHand
  793.     LEA        Counter,A0        ; point to the counter
  794.     ADDQ.L        #1,(A0)            ; Increment it
  795.     MOVE.B        vT1C(A1),D0        ; Clear the interrupt flag on the 6522
  796.     RTS
  797.  
  798. Counter            DC.L    1        ; The counter
  799. PrevIVC            DC.L    0        ; Previous interrupt vector
  800.  
  801.     END
  802.